/**
 * 清理服务
 * 负责定期清理临时文件、过期会话和媒体文件
 */

const fs = require('fs').promises;
const path = require('path');
const schedule = require('node-schedule');
const config = require('../config');
const logger = require('../utils/logger');

// 清理任务列表
const cleanupJobs = {};

/**
 * 初始化清理服务
 */
const initialize = async () => {
  try {
    // 确保清理目录都存在
    await ensureDirectoriesExist();
    
    // 安排清理任务
    scheduleCleanupTasks();
    
    logger.info('清理服务初始化成功');
    return true;
  } catch (error) {
    logger.error('清理服务初始化失败:', error);
    return false;
  }
};

/**
 * 确保所有需要的目录都存在
 */
const ensureDirectoriesExist = async () => {
  const directories = [
    path.resolve(config.whatsapp.dataPath),
    path.resolve(config.whatsapp.mediaStoragePath),
    path.resolve(config.uploads.uploadDir),
    path.resolve(config.uploads.tempDir),
    path.resolve(config.logging.directory)
  ];
  
  for (const dir of directories) {
    try {
      await fs.mkdir(dir, { recursive: true });
      logger.debug(`确保目录存在: ${dir}`);
    } catch (error) {
      logger.error(`创建目录失败: ${dir}`, error);
      throw error;
    }
  }
};

/**
 * 安排清理任务
 */
const scheduleCleanupTasks = () => {
  // 清理临时文件 - 每小时
  cleanupJobs.tempFiles = schedule.scheduleJob('0 * * * *', async () => {
    try {
      await cleanupTempFiles();
      logger.info('临时文件清理完成');
    } catch (error) {
      logger.error('清理临时文件失败:', error);
    }
  });
  
  // 清理媒体文件 - 每天凌晨3点
  cleanupJobs.mediaFiles = schedule.scheduleJob('0 3 * * *', async () => {
    try {
      await cleanupMediaFiles();
      logger.info('过期媒体文件清理完成');
    } catch (error) {
      logger.error('清理媒体文件失败:', error);
    }
  });
  
  // 清理过期WhatsApp会话 - 每天凌晨4点
  cleanupJobs.whatsappSessions = schedule.scheduleJob('0 4 * * *', async () => {
    try {
      await cleanupWhatsappSessions();
      logger.info('过期WhatsApp会话清理完成');
    } catch (error) {
      logger.error('清理WhatsApp会话失败:', error);
    }
  });
  
  // 压缩旧日志文件 - 每周一凌晨5点
  cleanupJobs.logs = schedule.scheduleJob('0 5 * * 1', async () => {
    try {
      await rotateLogFiles();
      logger.info('日志文件轮换完成');
    } catch (error) {
      logger.error('轮换日志文件失败:', error);
    }
  });
  
  logger.info('所有清理任务已调度');
};

/**
 * 清理临时文件
 */
const cleanupTempFiles = async () => {
  const tempDir = path.resolve(config.uploads.tempDir);
  
  try {
    const files = await fs.readdir(tempDir);
    const now = Date.now();
    let deletedCount = 0;
    
    for (const file of files) {
      const filePath = path.join(tempDir, file);
      
      try {
        const stats = await fs.stat(filePath);
        
        // 清理超过1小时的文件
        const fileAge = now - stats.mtime.getTime();
        if (fileAge > config.uploads.cleanupInterval) {
          await fs.unlink(filePath);
          deletedCount++;
        }
      } catch (fileError) {
        logger.warn(`清理临时文件时出错: ${filePath}`, fileError);
      }
    }
    
    logger.info(`已清理 ${deletedCount} 个临时文件`);
  } catch (error) {
    logger.error('读取临时目录失败:', error);
    throw error;
  }
};

/**
 * 清理过期媒体文件
 */
const cleanupMediaFiles = async () => {
  const mediaDir = path.resolve(config.whatsapp.mediaStoragePath);
  
  try {
    const files = await getAllFiles(mediaDir);
    const now = Date.now();
    const expireTime = config.whatsapp.mediaExpireDays * 24 * 60 * 60 * 1000;
    let deletedCount = 0;
    
    for (const filePath of files) {
      try {
        const stats = await fs.stat(filePath);
        
        // 清理超过指定天数的文件
        const fileAge = now - stats.mtime.getTime();
        if (fileAge > expireTime) {
          await fs.unlink(filePath);
          deletedCount++;
        }
      } catch (fileError) {
        logger.warn(`清理媒体文件时出错: ${filePath}`, fileError);
      }
    }
    
    logger.info(`已清理 ${deletedCount} 个过期媒体文件`);
  } catch (error) {
    logger.error('读取媒体目录失败:', error);
    throw error;
  }
};

/**
 * 清理过期WhatsApp会话
 */
const cleanupWhatsappSessions = async () => {
  const sessionsDir = path.resolve(config.whatsapp.dataPath);
  
  try {
    const directories = await fs.readdir(sessionsDir);
    const now = Date.now();
    const expireTime = config.whatsapp.sessionTimeout;
    let deletedCount = 0;
    
    for (const dir of directories) {
      const dirPath = path.join(sessionsDir, dir);
      
      try {
        const stats = await fs.stat(dirPath);
        
        // 只处理目录
        if (stats.isDirectory()) {
          // 检查最后修改时间
          const dirAge = now - stats.mtime.getTime();
          
          // 如果目录超过会话超时时间未修改，则认为是过期会话
          if (dirAge > expireTime) {
            // 递归删除目录
            await removeDirectory(dirPath);
            deletedCount++;
          }
        }
      } catch (dirError) {
        logger.warn(`清理WhatsApp会话时出错: ${dirPath}`, dirError);
      }
    }
    
    logger.info(`已清理 ${deletedCount} 个过期WhatsApp会话`);
  } catch (error) {
    logger.error('读取会话目录失败:', error);
    throw error;
  }
};

/**
 * 轮换日志文件
 */
const rotateLogFiles = async () => {
  const logsDir = path.resolve(config.logging.directory);
  
  try {
    const files = await fs.readdir(logsDir);
    let rotatedCount = 0;
    
    for (const file of files) {
      // 只处理日志文件
      if (file.endsWith('.log')) {
        const filePath = path.join(logsDir, file);
        const stats = await fs.stat(filePath);
        
        // 如果文件大于配置的最大大小，进行压缩
        if (stats.size > parseSize(config.logging.maxSize)) {
          const timestamp = new Date().toISOString().replace(/:/g, '-');
          const archivePath = path.join(logsDir, `${file}.${timestamp}.gz`);
          
          try {
            // 在实际实现中，这里应该添加压缩文件的代码
            // 由于代码简化，这里只是模拟重命名
            await fs.rename(filePath, archivePath);
            rotatedCount++;
          } catch (archiveError) {
            logger.warn(`压缩日志文件时出错: ${filePath}`, archiveError);
          }
        }
      }
    }
    
    logger.info(`已轮换 ${rotatedCount} 个日志文件`);
  } catch (error) {
    logger.error('读取日志目录失败:', error);
    throw error;
  }
};

/**
 * 递归获取目录下所有文件
 * @param {string} directory 目录路径
 * @returns {Promise<string[]>} 文件路径数组
 */
const getAllFiles = async (directory) => {
  let results = [];
  
  try {
    const items = await fs.readdir(directory);
    
    for (const item of items) {
      const itemPath = path.join(directory, item);
      const stats = await fs.stat(itemPath);
      
      if (stats.isDirectory()) {
        const subResults = await getAllFiles(itemPath);
        results = results.concat(subResults);
      } else {
        results.push(itemPath);
      }
    }
  } catch (error) {
    logger.warn(`获取目录内容时出错: ${directory}`, error);
  }
  
  return results;
};

/**
 * 递归删除目录
 * @param {string} directory 目录路径
 */
const removeDirectory = async (directory) => {
  try {
    const items = await fs.readdir(directory);
    
    for (const item of items) {
      const itemPath = path.join(directory, item);
      const stats = await fs.stat(itemPath);
      
      if (stats.isDirectory()) {
        await removeDirectory(itemPath);
      } else {
        await fs.unlink(itemPath);
      }
    }
    
    await fs.rmdir(directory);
  } catch (error) {
    logger.warn(`删除目录时出错: ${directory}`, error);
    throw error;
  }
};

/**
 * 解析日志大小配置
 * @param {string} size 大小字符串，如 "10m"
 * @returns {number} 字节数
 */
const parseSize = (size) => {
  const units = { k: 1024, m: 1024 * 1024, g: 1024 * 1024 * 1024 };
  const match = (size || '').toLowerCase().match(/^(\d+)([kmg])?$/);
  
  if (!match) {
    return 5 * 1024 * 1024; // 默认5MB
  }
  
  const num = parseInt(match[1], 10);
  const unit = match[2] || '';
  
  return num * (units[unit] || 1);
};

/**
 * 停止所有清理任务
 */
const shutdown = () => {
  logger.info('停止所有清理任务');
  
  Object.values(cleanupJobs).forEach(job => {
    if (job) {
      job.cancel();
    }
  });
};

// 直接清理函数，可以手动调用
const manualCleanup = {
  tempFiles: cleanupTempFiles,
  mediaFiles: cleanupMediaFiles,
  whatsappSessions: cleanupWhatsappSessions,
  logFiles: rotateLogFiles
};

module.exports = {
  initialize,
  shutdown,
  manualCleanup
}; 